#include "shader.h"
#include "vertexbuffer.h"
#include "misc.h"


shader::shader()
{
}


shader::~shader()
{
}

GLuint shader::CompileShader(GLenum shaderType, const char * shaderPath)
{
	GLuint shader = glCreateShader(shaderType);
	if (shader == 0)
	{
		printf("glCreateShader fail\n");
		return 0;
	}
	int filesize = 0;
	const char* shaderCode = LoadFileContent(shaderPath, filesize);
	if (shaderCode == nullptr)
	{
		printf("load shader code from file : %s fail\n", shaderPath);
		glDeleteShader(shader);
		return 0;
	}
	glShaderSource(shader, 1, &shaderCode, nullptr);
	glCompileShader(shader);
	GLint compileResult = GL_TRUE;
	glGetShaderiv(shader, GL_COMPILE_STATUS, &compileResult);
	if (compileResult == GL_FALSE)
	{
		char szLog[1024] = { 0 };
		GLsizei logLen = 0;
		glGetShaderInfoLog(shader, 1024, &logLen, szLog);
		printf("Compile Shader fail error log : %s \nshader code :\n%s\n", szLog, shaderCode);
		glDeleteShader(shader);
		shader = 0;
	}
	delete shaderCode;
	return shader;
}

GLuint shader::CreateGPUProgram(GLuint vsShader, GLuint fsShader)
{
	GLuint program = glCreateProgram();
	glAttachShader(program, vsShader);
	glAttachShader(program, fsShader);
	glLinkProgram(program);
	glDetachShader(program, vsShader);
	glDetachShader(program, fsShader);
	glDeleteShader(vsShader);
	glDeleteShader(fsShader);
	GLint linkResult = GL_TRUE;
	glGetProgramiv(program, GL_LINK_STATUS, &linkResult);
	if (linkResult == GL_FALSE)
	{
		char szLog[1024] = { 0 };
		GLsizei logLen = 0;
		glGetProgramInfoLog(program, 1024, &logLen, szLog);
		printf("link program fail error log : %s \n", szLog);
		glDeleteProgram(program);
		program = 0;
	}
	return program;
}

void shader::SetTexure(const char * name, const char * texturePath)
{
	auto iter = mUniformTextures.find(name);
	if (iter==mUniformTextures.end())
	{
		GLuint location = glGetUniformLocation(mProgram, name);
		if (location != -1)
		{
			UniformTexture *t = new UniformTexture();
		
			t->mLocation = location;
			t->mTexture = CreateTexture2DFromBMP(texturePath);
			mUniformTextures.insert(std::pair<std::string, UniformTexture*>(name, t));
		}
	}
	else
	{
		glDeleteTextures(1,&iter->second->mTexture);
		iter->second->mTexture = CreateTexture2DFromBMP(name);
	}
	/*if (mTexture.mLocation==-1)
	{
		GLuint location = glGetUniformLocation(mProgram, name);
		if (location!=-1)
		{
			mTexture.mLocation = location;
			mTexture.mTexture = CreateTexture2DFromBMP(texturePath);
		}
	}*/
}

void shader::SetCameraPos(float x, float y, float z)
{
	//glUniform4f(mCameraPosLocation,50,50,10,0);
}

void shader::Init(const char * vsShader, const char * fsShader)
{
	
	GLuint vs = CompileShader(GL_VERTEX_SHADER, vsShader);
	if (vsShader == 0) {
		return;
	}
	GLuint fs = CompileShader(GL_FRAGMENT_SHADER, fsShader);
	if (fsShader == 0) {
		return;
	}
	mProgram = CreateGPUProgram(vs, fs);
	if (mProgram != 0) {
		mModelMatrixLocation = glGetUniformLocation(mProgram, "ModelMatrix");
		mViewMatrixLocation = glGetUniformLocation(mProgram, "ViewMatrix");
		mProjectionMatrixLocation = glGetUniformLocation(mProgram, "ProjectionMatrix");
		NormalMatrixLocation = glGetUniformLocation(mProgram, "NormalMatrix");
		mCameraPosLocation = glGetUniformLocation(mProgram, "U_CameraPos");

		mPositionLocation = glGetAttribLocation(mProgram, "position");
		mColorLocation = glGetAttribLocation(mProgram, "color");
		mNormalLocation = glGetAttribLocation(mProgram,"normal");
		mTexcoordLocation = glGetAttribLocation(mProgram, "texcoord");
	}
}

void shader::Bind(float * M, float * V, float * P)
{
	glm::mat4 model = glm::translate(0.0f, 0.0f, -4.0f);
	glm::mat4 normalMatrix = glm::inverseTranspose(model);

	glUseProgram(this->mProgram);
	glUniformMatrix4fv(this->mModelMatrixLocation, 1, GL_FALSE, M);
	glUniformMatrix4fv(this->mViewMatrixLocation, 1, GL_FALSE, V);
	glUniformMatrix4fv(this->mProjectionMatrixLocation, 1, GL_FALSE, P);
	glUniformMatrix4fv(NormalMatrixLocation, 1, GL_FALSE, glm::value_ptr(normalMatrix));
	
	//
	int index = 0;
	for (auto iter = mUniformTextures.begin();iter!=mUniformTextures.end();iter++)
	{
		glActiveTexture(GL_TEXTURE0+index);
		glBindTexture(GL_TEXTURE_2D, iter->second->mTexture);
		glUniform1i(iter->second->mLocation, index++);
	}
	/*if (mTexture.mLocation!=-1)
	{
		glBindTexture(GL_TEXTURE_2D,mTexture.mTexture);
		glUniform1i(mTexture.mLocation, 0);
	}*/
	glEnableVertexAttribArray(mPositionLocation);
	glVertexAttribPointer(mPositionLocation,4,GL_FLOAT,GL_FALSE,sizeof(VertexA),0);

	glEnableVertexAttribArray(mColorLocation);
	glVertexAttribPointer(mColorLocation, 4, GL_FLOAT, GL_FALSE, sizeof(VertexA), (void*)(sizeof(float)*4));

	glEnableVertexAttribArray(mTexcoordLocation);
	glVertexAttribPointer(mTexcoordLocation, 4, GL_FLOAT, GL_FALSE, sizeof(VertexA), (void*)(sizeof(float) * 8));

	glEnableVertexAttribArray(mNormalLocation);
	glVertexAttribPointer(mNormalLocation, 4, GL_FLOAT, GL_FALSE, sizeof(VertexA), (void*)(sizeof(float) * 12));

	
}
